#pragma once
#include "../mpp_workshop/IBarrier.h"
#include "../mpp_workshop/cpp_framework.h"
#include <assert.h>

using namespace CMDR;

#define LEFT_CHILD_ID 1 // odd
#define RIGHT_CHILD_ID 0 // even

static int ThreadID = 0;

class BSTBThreadLocalSense : public ThreadLocal<int>
{
public:
	int initialValue() { return 0; }
};

class BSTBThreadLocalID : public ThreadLocal<int>
{
public:
	int initialValue() { return 0; }
};

//volatile int global_sense; // global barrier sense

class Node;
class BinaryStaticTreeBarrier;

class BinaryStaticTreeBarrier :
	public IBarrier
{
	friend class Node;
public: // BinaryStaticTreeBarrier Class

	class Node
	{
		friend class BinaryStaticTreeBarrier;
	public: // Node Class

		void initNode(Node* _parent)
		{
			//global_sense = 1; // set global to 1 since all threads would get NULL==0 as init.
			this->children = 0;
			this->leftChildNotDone.set(true);
			this->rightChildNotDone.set(true);
			this->parent = _parent;
		}

		/*
		* C'tor
		* parent - the parent node of this new node.
		*/
		Node(Node* _parent)
		{
			initNode(_parent);
		}


		// C'tor to construct root node
		Node()
		{
			initNode(NULL);
		}

		// d'tor
		virtual ~Node(void)
		{
		}

		void await();

		// increaments the  
		inline void addChild()
		{
			children++;
			// Perform check (this should be called only during the tree building 
			// process so we don't care it takes a little longer.

			assert(children <= 2); //ERROR: Parent has more than 2 children in a Binary tree
		}

		inline const int getChildren()
		{
			return children; 
		}

		/**
		* @param childThreadId Thread ID of the caller.
		*/
		void markChildDone(int childThreadId);

    private: // Node Class
		Node* parent;
		//volatile int global_sense; // global barrier sense

	    int children; // number of children
		AtomicBoolean leftChildNotDone;
		AtomicBoolean rightChildNotDone;
    
	}; // end of inner class Node

	// C'tor for BinaryStaticTreeBarrier
	// numOfThreads - number of threads the barrier is for.
	BinaryStaticTreeBarrier(int numOfThreads)
	{
		_numOfThreads = numOfThreads;	
		_node = new Node*[numOfThreads];
		global_sense = 1; // set global to 1 since all threads would get NULL==0 as init.
		int depth = 0;
		
		int n = _numOfThreads;
		while (n > 1)
		{
			depth++;
			n = n / 2;
		}

		//while (n > 1) {
		//	depth++;
		//	n >>= 1;
		//}

		_node[0] = new Node(NULL);
		build(depth);
	}

	virtual ~BinaryStaticTreeBarrier(void)
	{
		delete[] _node;
		_node = NULL;
	}

	/*
	 * inits that _threadID and _threadSense thread specifics.
	 */
	inline virtual void initThreadLocals(int me)
	{
		threadId.set(me);
	}

	inline virtual char* getBarrierName()
	{
		return "BinaryStaticBarrier";
	}

	/* Barrier's await impl */
	virtual void await();

private: // StaticBinaryStaticTreeBarrier Class
	Node** _node;  // array of leaf nodes  instead of this - use stl::vector of POINTER to Node. delete in vector invalidates the iterator.
	int _numOfThreads;

	BSTBThreadLocalSense threadSense; // thread-local sense
	BSTBThreadLocalID threadId;

	volatile int global_sense; // global barrier sense

	// recursive tree constructor
	void build(int depth);

	const int getParentIdx(int childIndex);

	/**
	* @param parentIdx
	* @return left child index in _node of given parentIdx
	*/
	const int getLeftChildIdx(int parentIdx);

	/**
	* @param parentIdx
	* @return right child index in _node of given parentIdx
	*/
	const int getRightChildIdx(int parentIdx);
};
